/*
 * @(#) GameScreen.java
 *
 * Copyright 2006 - 2009 by the original authors of Galaxy
 * and all its contributors ("Galaxy.org").
 *
 * All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package kyodai;

import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.GameCanvas;
import javax.microedition.lcdui.game.Sprite;

/**
 * The game screen, from splash to over.
 * 
 * @author Cheng Liang
 * @version 1.0
 */
public class GameScreen extends GameCanvas implements Runnable, CommandListener {
	
	/** The menu items. */
    public String[] item = 
    	{"新游戏", "载入游戏", "难度：中等", "高分排行榜", "帮助", "关于", "退出"};
	
    /** One checker size. */
	public static final int CHECKER_SIZE = 22;
    
    /** The menu item saved. */
    private String[] menuItem = item;
    
	/** The game manager. */
	private GameInputManager mgr;

	/** Kyodai application. */
	private KyodaiApp app;
	
	/** Graphics to paint. */
	private Graphics g;
	
	/** Resource handler. */
	private Resource res = Resource.getInstance();
	
	/** Splash background image. */
	private Image splashBackground;
	
	/** The main background image. */
	private Image main;
	
	/** Top image. */
	private Image top;
	
	/** Timer image. */
	private Image timer;
	
	/** The selector. */
	private Image selector;
	
	/** The last selector. */
	private Image lastSelector;
	
	/** The number of kinds of tools. */
	private Image[] num;
	
	/** The checkers. */
	private Image[] checkers;
	
	/** Back command. */
	private Command ok = new Command("确定", Command.OK, 1);
	
	/** True if the thread is running. */
	private boolean running;
	
	/** The row number. */
	private int row;
	
	/** The column number. */
	private int col;
	
	/** Chess board width, the value is CHESSER_SIZE * row. */
	private int chessboardWidth;
	
	/** Chess board height, the value is CHESSER_SIZE * col. */
	private int chessboardHeight;
	
	/** Chess board position X, the value is (screenWidth - chessboardWidth) / 2; */
	private int chessboardX;
	
	/** Chess board position Y, the value is (screenHeight - chessboardHeight) / 2; */
	private int chessboardY;
	
	/** Selected border offset X. */
	private int selectOffsetX;
	
	/** Selected border offset Y. */
	private int selectOffsetY;
	
	/** Last selected border position. */
	private Point lastSelected;
	
	/** Current state. */
	private int state = Resource.SOUND_SCREEN;
	
	/** Last state. This will be set as the last state before state changes. */
	private int lastState = Resource.SOUND_SCREEN;
	
	/** Active index of menus. */
	private int activeIndex;
	
	/** Splash frame count. */
	private int splashIndex;
	
	/**
	 * Instantiates a new game screen.
	 * 
	 * @param app the application
	 */
	public GameScreen(KyodaiApp app) {
		super(false);
		this.app = app;
		this.mgr = new GameInputManager(this);
		
		setFullScreenMode(true);
		splashBackground = res.createImage("/splash.png");
    	
		g = getGraphics();
		g.setClip(0, 0, getWidth(), getHeight());
	}
	
	/**
	 * Starts this screen thread to process input and repaint canvas.
	 */
	public void start() {
		running = true;
		new Thread(this).start();
	}

	/**
	 * Stops this thread.
	 */
	public void stop() {
		running = false;
	}

	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
	public void run() {
		while (running) {
			long startTime = System.currentTimeMillis();
			if(activeIndex < 0 || activeIndex > item.length - 1) {
				activeIndex = 0;
			}
			changeState();
			mgr.processInput();
			long duration = System.currentTimeMillis() - startTime;
			if (duration < 50) {
				try {
					Thread.sleep(50 - duration);
				} catch (InterruptedException e) {
					// NO ACTION
					// because Thread.interrupt() does not exist
				}
			} else {
				Thread.yield();
			}
		}
	}
	
	/**
	 * Paint sound on or off screen.
	 */
	private void paintSound() {
		stop();
		Form form = new Form("声音控制");
		form.append("是否打开游戏声音？");
		final Command on = new Command("开启", Command.ITEM, 1);
		final Command off = new Command("关闭", Command.ITEM, 1);
		form.addCommand(on);
		form.addCommand(off);
		form.setCommandListener(new CommandListener() {

			public void commandAction(Command cmd, Displayable displayable) {
				if(cmd == on) {
					res.startBackgroundSound();
				}
				setState(Resource.SPLASH_SCREEN);
				app.setCurrentScreen(app.gameScreen);
				start();
			}
			
		});
		app.setCurrentScreen(form);
	}

	/**
	 * Paint splash screen.
	 */
	private void paintSplash() {
        g.drawImage(splashBackground, 0, 0, Graphics.TOP | Graphics.LEFT);
        g.setColor(0xFFFFFF);
        g.fillRect(0, 0, getWidth(), getHeight() - splashIndex * getHeight() / 50);
        flushGraphics();
	}
	
	/**
	 * Paint menu screen.
	 */
	private void paintMenu() {
		if(item.length != menuItem.length) {
			item = menuItem;
			activeIndex = 0;
		}
		g.drawImage(splashBackground, 0, 0, Graphics.TOP | Graphics.LEFT);
        g.setColor(0x333333);
        Font font = Resource.FONT_MONO_BOLD_LARGE;
        g.setFont(font);
        int fontHeight = font.getHeight();
        int hcenter = getWidth() >> 1;
        for (int i = 0, len = item.length; i < len; i++) {
            if(i == activeIndex) {
                g.setColor(0xFF0000);
            } else {
                g.setColor(0x000000);
            }
            g.drawString(item[i], hcenter, 100 + i * fontHeight, 
            		Graphics.TOP | Graphics.HCENTER);
        }
        flushGraphics();
	}
	
	/**
	 * Paint game end screen.
	 * 
	 * @param endType 3 types:<ul> 
	 * <li>Resource.GAME_LOSE - game lose;</li>
	 * <li>Resource.GAME_PAUSE - game pause;</li>
	 * <li>Resource.GAME_WIN - game win;</li></ul>
	 */
	private void paintEnd(int endType) {
		String title = "";
		switch(endType) {
		case Resource.GAME_PAUSE:
			title = "游戏暂停";
			item = new String[3];
			item[0] = "继续游戏";
        	item[1] = "返回主菜单";
        	item[2] = "退出";
			break;
		case Resource.GAME_WIN:
			title = "你赢了!";
			item = new String[4];
        	item[0] = "继续游戏";
        	item[1] = "重新开始";
        	item[2] = "返回主菜单";
        	item[3] = "退出";
			break;
		case Resource.GAME_LOSE_TIME_OUT:
			title = "时间到, 你败了!";
			item = new String[3];
			item[0] = "重新开始";
        	item[1] = "返回主菜单";
        	item[2] = "退出";
        	break;
		case Resource.GAME_LOSE_RESET_OVER:
			title = "已经没有重排机会, 你败了!";
			item = new String[3];
			item[0] = "重新开始";
        	item[1] = "返回主菜单";
        	item[2] = "退出";
			break;
		}
		g.setColor(255, 255, 255);
        g.fillRect(0, 0, getWidth(), getHeight());
        Font font = Resource.FONT_MONO_BOLD_LARGE;
        g.setFont(font);
        int fontHeight = font.getHeight();
        int hcenter = getWidth() >> 1;
        g.setColor(0xFF0000);
        g.drawString(title, hcenter, 50, Graphics.HCENTER | Graphics.TOP);
        for (int i = 0, len = item.length; i < len; i++) {
            if(i == activeIndex) {
                g.setColor(0xFF0000);
            } else {
                g.setColor(0x000000);
            }
            g.drawString(item[i], hcenter, 120 + i * fontHeight, Graphics.TOP | Graphics.HCENTER);
        }
        flushGraphics();
	}
	
	/**
	 * Paint help screen.
	 */
	private void paintHelp() {
		stop();
		Form helpScreen = new Form("游戏说明");
		helpScreen.append("连连看 v1.0.0\n");
		helpScreen.append("--规则--\n");
		helpScreen.append("游戏开始时, 地图上会有数张不同的图片随机分布. 游戏者需要在地图上找出两张相同的图片, 并且这两个点之前可以用不超过 3 条的直线连接起来, 这样可以消除这两点的图片. 如此直到地图上所有的图片全部消除.\n");
		helpScreen.append("--操作--\n");
		helpScreen.append("键盘操作: 方向键移动选择框; 导航键选择; 1-提示; 3-重新排列.\n");
		helpScreen.append("--制作--\n");
		helpScreen.append("Cheng Liang, galaxy.org");
        helpScreen.addCommand(ok);
        helpScreen.setCommandListener(this);
        app.setCurrentScreen(helpScreen);
	}
	
	/**
	 * Paint about screen.
	 */
	private void paintAbout() {
		stop();
		Form aboutScreen = new Form("关于");
		aboutScreen.append("连连看 v1.0.0\n");
		aboutScreen.append("版权所有 (C) galaxy.org 2009\n");
		aboutScreen.append("本游戏在 Apache License 2.0 协议下开放源代码.本游戏源代码可以在http://gamehouse.googlecode.com/svn/trunk/Kyodai找到.\n");
		aboutScreen.append("----------\n");
		aboutScreen.append("Copyright (C) galaxy.org 2009\n");
		aboutScreen.append("This game's code is opened under Apache License 2.0. You can get code from http://gamehouse.googlecode.com/svn/trunk/Kyodai.");
		aboutScreen.addCommand(ok);
		aboutScreen.setCommandListener(this);
		app.setCurrentScreen(aboutScreen);
	}
	
	/**
	 * Paint difficulty chosen screen.
	 */
	private void paintDifficuty() {
		app.model.level++;
		if(app.model.level > Resource.HARD) {
			app.model.level = Resource.EASY;
		}
		init();
		item[2] = null;
		switch(app.model.level) {
		case Resource.EASY:
			item[2] = "难度：简单";
			break;
		case Resource.NORMAL:
			item[2] = "难度：中等";
			break;
		case Resource.HARD:
			item[2] = "难度：困难";
			break;
		}
	}
	
	/**
	 * Paint playing screen.
	 */
	private void paintPlay() {
		int screenWidth = getWidth();
		int screenHeight = getHeight();
		g.setClip(0, 0, screenWidth, screenHeight);
		// main canvas
		int[][] map = app.model.map;
		g.drawImage(main, 0, 0, Graphics.LEFT | Graphics.TOP);
        g.setColor(0x000000);
        g.setFont(Resource.FONT_MONO_BOLD_LARGE);
        String section = "正常";
        switch(app.model.section) {
        case 2: // to top
            section = "向上";
            break;
        case 3: // to bottom
        	section = "向下";
            break;
        case 4: // to left
        	section = "向左";
            break;
        case 5: // to right
        	section = "向右";
            break;
        }
        g.drawString(section, screenWidth - 5, 45, Graphics.TOP | Graphics.RIGHT);
        g.setColor(0xFF0000);
        g.drawRect(chessboardX - 1, chessboardY - 1, chessboardWidth + 1, chessboardHeight + 1);
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (map[i][j] != 0) {
                    g.drawImage(checkers[map[i][j] - 1], chessboardX + i * CHECKER_SIZE, chessboardY + j * CHECKER_SIZE, Graphics.LEFT | Graphics.TOP);
                }
            }
        }
        g.drawImage(selector, chessboardX + selectOffsetX, chessboardY + selectOffsetY, Graphics.LEFT | Graphics.TOP);
        if(lastSelected != null) {
        	g.drawImage(lastSelector, chessboardX + lastSelected.x * CHECKER_SIZE, chessboardY + lastSelected.y * CHECKER_SIZE, Graphics.LEFT | Graphics.TOP);
        }
        // footer
        g.setColor(0x000000);
		g.setFont(Resource.FONT_MONO_BOLD_SMALL);
		g.drawString("1- 提示; 3- 重排; *- 返回主菜单", 0, 300, Graphics.TOP | Graphics.LEFT);
		// top
		g.drawImage(top, 0, 0, Graphics.LEFT | Graphics.TOP);
        g.drawImage(num[app.model.hintCnt], 90, 8, Graphics.LEFT | Graphics.TOP);
        g.drawImage(num[app.model.resetCnt], 205, 8, Graphics.LEFT | Graphics.TOP);
        // timer
		g.drawImage(timer, 5, 50, Graphics.TOP | Graphics.LEFT);
		g.setColor(0xFFFFFF);
		g.fillRect(170 - app.model.currTime * 170 / Resource.TOTAL_TIME, 50, app.model.currTime * 170 / Resource.TOTAL_TIME, 20);
		flushGraphics();
	}
	
	/**
	 * Paint high score screen.
	 */
	private void paintHighScore() {
		alert("高分排行榜");
	}
	
	/**
	 * Paint load record screen.
	 */
	private void paintLoad() {
		alert("载入进度");
	}
	
	/**
	 * Exit game.
	 */
	private void exit() {
		app.destroyApp(true);
	}
	
	/**
	 * Show an alert message box which has two buttons, one is OK, the other one
	 * is back. You can do what you want with OK button, but the back button 
	 * always back to the last screen that came to the alert.
	 * 
	 * @param content message to display on this alert box
	 */
	private void alert(String content) {
		setState(Resource.GAME_ALERT);
		Alert alert = new Alert("标题", content, null, AlertType.INFO);
    	alert.addCommand(ok);
    	alert.setCommandListener(this);
    	stop();
    	app.setCurrentScreen(alert);
	}
	
	/* (non-Javadoc)
	 * @see javax.microedition.lcdui.CommandListener#commandAction(javax.microedition.lcdui.Command, javax.microedition.lcdui.Displayable)
	 */
	public void commandAction(Command cmd, Displayable displayable) {
		if(cmd == ok) {
			setState(lastState);
			app.setCurrentScreen(app.gameScreen);
			start();
		}
	}
	
	/**
	 * Do OK.
	 */
	public void doOk() {
		switch(state) {
		case Resource.MENU_SCREEN:
			switch (activeIndex) {
            case 0: // start
            	init();
            	setState(Resource.PLAY_SCREEN);
                break;
            case 1: // load
            	setState(Resource.LOAD_SCREEN);
                break;
            case 2: // configuration
            	//setState(Resource.CONFIG_SCREEN);
            	paintDifficuty();
                break;
            case 3: // high score
            	setState(Resource.HIGH_SCORE_SCREEN);
                break;
            case 4: // help
            	setState(Resource.HELP_SCREEN);
                break;
            case 5: // about
            	setState(Resource.ABOUT_SCREEN);
                break;
            case 6: // exit
            	setState(Resource.EXIT_GAME);
                break;
			}
			break;
		case Resource.GAME_LOSE_RESET_OVER:
		case Resource.GAME_LOSE_TIME_OUT:
			switch(activeIndex) {
			case 0: // restart TODO
				init();
            	setState(Resource.PLAY_SCREEN);
				break;
			case 1: // menu
				setState(Resource.MENU_SCREEN);
				break;
			case 2: // exit
				setState(Resource.EXIT_GAME);
				break;
			}
			break;
		case Resource.GAME_PAUSE:
			switch(activeIndex) {
			case 0: // continue TODO
				
				break;
			case 1: // menu
				setState(Resource.MENU_SCREEN);
				break;
			case 2: // exit
				setState(Resource.EXIT_GAME);
				break;
			}
			break;
		case Resource.GAME_WIN:
			switch(activeIndex) {
			case 0: // continue TODO
				
				break;
			case 1: // restart TODO
				
				break;
			case 2: // menu
				setState(Resource.MENU_SCREEN);
				break;
			case 3: // exit
				setState(Resource.EXIT_GAME);
				break;
			}
			break;
		case Resource.PLAY_SCREEN:
			if (lastSelected == null) {
				int x = selectOffsetX / CHECKER_SIZE;
				int y = selectOffsetY / CHECKER_SIZE;
				if(app.model.map[x][y] != 0) {
					lastSelected = new Point(x, y);
				}
			} else {
				int x = selectOffsetX / CHECKER_SIZE;
				int y = selectOffsetY / CHECKER_SIZE;
				if(app.model.map[x][y] != 0) {
					Point currentSelected = new Point(x, y);
					playGame(currentSelected);
					currentSelected = null;
				}
			}
			break;
		}
	}
	
	/**
	 * Do up.
	 */
	public void doUp() {
		switch(state) {
		case Resource.MENU_SCREEN:
		case Resource.GAME_LOSE_TIME_OUT:
		case Resource.GAME_LOSE_RESET_OVER:
		case Resource.GAME_PAUSE:
		case Resource.GAME_WIN:
			if (activeIndex != 0) {
	            --activeIndex;
	        } else {
	            activeIndex = item.length - 1;
	        }
			break;
		case Resource.PLAY_SCREEN:
			selectOffsetY -= CHECKER_SIZE;
			if (selectOffsetY < 0) {
				selectOffsetY = chessboardHeight - CHECKER_SIZE;
			}
			break;
		}
	}
	
	/**
	 * Do down.
	 */
	public void doDown() {
		switch(state) {
		case Resource.MENU_SCREEN:
		case Resource.GAME_LOSE_TIME_OUT:
		case Resource.GAME_LOSE_RESET_OVER:
		case Resource.GAME_PAUSE:
		case Resource.GAME_WIN:
			if (activeIndex != item.length - 1) {
	            ++activeIndex;
	        } else {
	            activeIndex = 0;
	        }
			break;
		case Resource.PLAY_SCREEN:
			selectOffsetY += CHECKER_SIZE;
			if (selectOffsetY >= chessboardHeight) {
				selectOffsetY = 0;
			}
			break;
		}
	}
	
	/**
	 * Do right.
	 */
	public void doRight() {
		switch(state) {
		case Resource.PLAY_SCREEN:
			selectOffsetX += CHECKER_SIZE;
			if (selectOffsetX >= chessboardWidth) {
				selectOffsetX = 0;
			}
			break;
		}
	}
	
	/**
	 * Do left.
	 */
	public void doLeft() {
		switch(state) {
		case Resource.PLAY_SCREEN:
			selectOffsetX -= CHECKER_SIZE;
			if (selectOffsetX < 0) {
				selectOffsetX = chessboardWidth - CHECKER_SIZE;
			}
			break;
		}
	}
	
	public void doHint() {
		alert("Hint");
	}
	
	public void doReset() {
		alert("Reset");
	}
	
	public void doMenu() {
		setState(Resource.GAME_PAUSE);
	}
	
	/**
	 * States change to run this game.
	 */
	public void changeState() {
		switch(state) {
		case Resource.SOUND_SCREEN:
			paintSound();
			new Thread(new Runnable() {

				public void run() {
					loadResources();
					init();
				}
				
			}).start();
			break;
		case Resource.SPLASH_SCREEN:
			paintSplash();
			if(splashIndex++ == 50) {
				setState(Resource.MENU_SCREEN);
			}
			break;
		case Resource.MENU_SCREEN:
			paintMenu();
			break;
		case Resource.HELP_SCREEN:
			paintHelp();
			break;
		case Resource.ABOUT_SCREEN:
			paintAbout();
			break;
		case Resource.CONFIG_SCREEN:
			// paintConfig();
			break;
		case Resource.PLAY_SCREEN:
			if(app.model.currTime++ == Resource.TOTAL_TIME) {
				setState(Resource.GAME_LOSE_TIME_OUT);
			}
			paintPlay();
			break;
		case Resource.HIGH_SCORE_SCREEN:
			paintHighScore();
			break;
		case Resource.LOAD_SCREEN:
			paintLoad();
			break;
		case Resource.EXIT_GAME:
			exit();
			break;
		case Resource.GAME_LOSE_TIME_OUT:
		case Resource.GAME_LOSE_RESET_OVER:
		case Resource.GAME_PAUSE:
		case Resource.GAME_WIN:
			paintEnd(state);
			break;
		}
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.microedition.lcdui.Canvas#keyPressed(int)
	 */
	protected void keyPressed(int keyCode) {
		mgr.doKeyPressed(keyCode, getGameAction(keyCode));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.microedition.lcdui.Canvas#keyReleased(int)
	 */
	protected void keyReleased(int keyCode) {
		mgr.doKeyReleased(keyCode, getGameAction(keyCode));
	}
	
	/**
	 * Sets the state.
	 * 
	 * @param state the new state
	 */
	private void setState(int state) {
		lastState = this.state;
		this.state = state;
	}
	
	private void playGame(Point currentSelected) {
        GameModel game = app.model;
        if (game.checkMatch(lastSelected, currentSelected)) {
            game.erase(lastSelected, currentSelected);
            if(game.currTime >= 40) {
            	game.currTime -= 40;
            }
            lastSelected = null;
            flushGraphics();
            if (game.restCheckers == 0) {
            	setState(Resource.GAME_WIN);
//                app.gameEnd("恭喜您已经完成游戏!", Resource.END_WIN); // FIXME
                currentSelected = null;
            } else if (game.restCheckers > 2 && game.findMatch(null) == null) {
            	if(game.resetCnt != 0) {
            		alert("您已经无路可走, 自动进行一次重排!");
        			game.reset();
            	} else {
            		alert("您已经没有重排机会!");
            		setState(Resource.GAME_LOSE_RESET_OVER);
//            		app.gameEnd("您已经无路可走!", Resource.END_LOSE); // FIXME
            	}
            }
        } else {
        	lastSelected = currentSelected;
        }
    }
	
	private void loadResources() {
		main = res.createImage("/main.png");
		top = res.createImage("/top.png");
		timer = res.createImage("/timer.png");
		num = new Image[4];
		Image number = res.createImage("/number.gif");
		num[0] = Image.createImage(number, 0, 0, 17, 24, Sprite.TRANS_NONE);
		num[1] = Image.createImage(number, 17, 0, 17, 24, Sprite.TRANS_NONE);
		num[2] = Image.createImage(number, 34, 0, 17, 24, Sprite.TRANS_NONE);
		num[3] = Image.createImage(number, 52, 0, 17, 24, Sprite.TRANS_NONE);
		Image tiles = res.createImage("/tile2.png");
		checkers = new Image[25];
    	for (int i = 0; i < 25; i++) {
    		checkers[i] = Image.createImage(tiles, i * CHECKER_SIZE, 0, CHECKER_SIZE, CHECKER_SIZE, Sprite.TRANS_NONE);
    	}
    	Image selectors = res.createImage("/selector.gif");
    	selector = Image.createImage(selectors, 0, 0, 22, 22, Sprite.TRANS_NONE);
    	lastSelector = Image.createImage(selectors, 22, 0, 22, 22, Sprite.TRANS_NONE);
	}
	
	private void init() {
		app.gameModelInit();
		app.model.currTime = 0;
    	row = app.model.row;
        col = app.model.column;
		chessboardWidth = CHECKER_SIZE * row;
        chessboardHeight = CHECKER_SIZE * col;
		chessboardX = (getWidth() - chessboardWidth) >> 1;
        chessboardY = (getHeight() + 60 - chessboardHeight) >> 1;
        selectOffsetX = 0;
        selectOffsetY = 0;
	}
	
}
